package com.hrc.rpc.spi;


import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;


/**
 * 利用spi机制按需加载所需的类，方便扩展
 */
@Slf4j
public class ExtensionLoader {
    /**
     * 加载路径
     */
    private static final String DEFAULT_PATH = "META-INF/rpc/";
    /**
     * 路径前缀
     */
    private static final String[] prefixs = {DEFAULT_PATH};
    /**
     * bean定义信息 key: 定义的key value：具体类
     */
    private static final Map<String, Class<?>> extensionClassCache = new HashMap<>();
    /**
     * 缓存同一个接口的全部实现类 key 为文件名 约定为接口全名  value 为该文件中全部的实现类
     */
    private static final Map<String, Map<String, Class<?>>> extensionClassCaches = new HashMap<>();
    /**
     * 实例化的bean
     */
    private static final Map<String, Object> singletonsObject = new HashMap<>();
    private static final ExtensionLoader extensionLoader;

    static {
        extensionLoader = new ExtensionLoader();
    }

    private ExtensionLoader() {
    }

    public static ExtensionLoader getInstance() {
        return extensionLoader;
    }

    /**
     * 加载接口下的全部实现类的class
     */
    @SneakyThrows
    public void loadExtension(Class<?> clazz) {
        String clazzName = clazz.getName();
        ClassLoader classLoader = this.getClass().getClassLoader();
        HashMap<String, Class<?>> map = new HashMap<>();
        for (String prefix : prefixs) {
            String path = prefix + clazzName;
            Enumeration<URL> urls = classLoader.getResources(path);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                Properties properties = new Properties();
                properties.load(url.openStream());
                for (Map.Entry<Object, Object> entry : properties.entrySet()) {
                    String key = entry.getKey().toString();
                    String value = entry.getValue().toString();
                    extensionClassCache.put(key, Class.forName(value));
                    map.put(key, Class.forName(value));
                    log.info("key :{} ,value:{}", key, value);
                }
            }
        }
        extensionClassCaches.put(clazzName, map);
    }

    public <V> V get(String name) {
        if (!singletonsObject.containsKey(name)) {
            try {
                singletonsObject.put(name, extensionClassCache.get(name).newInstance());
            } catch (InstantiationException e) {
                log.error(e.getMessage());
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        return (V) singletonsObject.get(name);
    }

    /**
     * 获取接口下所有的类
     */
    public List<Object> gets(Class<?> clazz) {

        final String name = clazz.getName();
        if (!extensionClassCaches.containsKey(name)) {
            try {
                throw new ClassNotFoundException(clazz + "未找到");
            } catch (ClassNotFoundException e) {
                log.error(e.getMessage());
            }
        }
        final Map<String, Class<?>> stringClassMap = extensionClassCaches.get(name);
        List<Object> objects = new ArrayList<>();
        if (!stringClassMap.isEmpty()) {
            stringClassMap.forEach((k, v) -> {
                try {
                    objects.add(singletonsObject.getOrDefault(k, v.newInstance()));
                } catch (InstantiationException | IllegalAccessException e) {
                    log.error(e.getMessage());
                }
            });
        }

        return objects;
    }

}
